package com.zmn.mcc.admin.controller.menu;


import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.mcc.admin.controller.MccBaseController;
import com.zmn.mcc.business.interfaces.menu.MenuBService;
import com.zmn.mcc.cas.thymeleaf.MenuPermitManager;
import com.zmn.mcc.common.constant.MenuConsts;
import com.zmn.mcc.common.constant.RedisConsts;
import com.zmn.mcc.model.entity.menu.McMenu;
import com.zmn.mcc.model.entity.menu.McMenuQuery;
import com.zmn.mcc.model.entity.sys.McSys;
import com.zmn.mcc.services.interfaces.menu.MenuService;
import com.zmn.mcc.services.interfaces.sys.SysService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 菜单配置表基础查询Controller
 *
 * @author guoqiao
 * @since 2021-05-28 16:23:56
 */
@Slf4j
@RestController
@RequestMapping("/mcc/menu")
public class MenuListController extends MccBaseController {

    @Resource
    private MenuService menuService;

    @Resource
    private SysService sysService;

    @Resource
    private MenuBService menuBService;

    @Resource
    RedisTemplate redisTemplate;

    @Resource
    MenuPermitManager menuPermitManager;

    /**
     * 描述: 查询列表
     *
     * @author guoqiao
     * @since 2021-05-28 16:23:56
     */
    @RequestMapping("/list")
    public ModelAndView list(@ModelAttribute McMenuQuery query) {

        ModelAndView mv = new ModelAndView("menu/list");

        // 查询出所有的系统属性
        List<McSys> sysList = Optional.ofNullable(sysService.listAll(GlobalDict.STATUS_ENABLE)).orElse(Lists.newArrayList());

        mv.addObject("statusList", MenuConsts.listStatus());
        mv.addObject("vo", query);
        mv.addObject("sysList", sysList);
        return mv;
    }

    /**
     * 描述: 菜单管理列表树状结构
     *
     * @param keyword keyword
     * @param menuUrl menuUrl
     * @param status  status
     * @return com.zmn.common.dto2.ResponseDTO<java.util.List   <   com.zmn.mcc.model.entity.menu.McMenu>>
     * @author jiafei
     * @since 2021/6/2 14:49
     */
    @RequestMapping("/ajaxTree")
    public ResponseDTO<List<McMenu>> ajaxTree(@RequestParam(value = "keyword", required = false) String keyword,
                                              @RequestParam(value = "menuUrl", required = false) String menuUrl,
                                              @RequestParam(value = "status", required = false) Integer status) {
        McMenuQuery query = new McMenuQuery();
        query.setKeyword(keyword);
        query.setMenuUrl(menuUrl);
        query.setStatus(status);
        List<McMenu> items = menuService.listByQuery(query);
        return ResponseDTO.success(items);
    }

    /**
     * 描述: 详情
     *
     * @author guoqiao
     * @since 2021-05-28 16:23:56
     */
    @RequestMapping("/addEditView")
    public ModelAndView info(@RequestParam(value = "menuId", required = false) Integer menuId,
                             @RequestParam(value = "parentId", required = false) Integer parentId) {

        McMenu menu;
        if (NumberUtil.isNullOrZero(menuId)) {
            menu = new McMenu();
            menu.setMenuLevel(MenuConsts.MENU_LEVEL_FIRST_LEVEL);
            menu.setMenuType(MenuConsts.MENU_TYPE_WHOLE_KEY);
            menu.setParentId(MenuConsts.MENU_FIRST_LEVEL_PARENT_ID);
        } else {
            menu = menuService.findById(menuId);
        }
        // 上级菜单
        McMenu parentMenu = menuService.findById(parentId);
        if (!Objects.isNull(parentMenu)) {
            // 新增子菜单 传入父id
            menu.setParentId(parentMenu.getMenuId());
        }

        // 查询出所有的系统属性
        List<McSys> sysList = Optional.ofNullable(sysService.listAll(GlobalDict.STATUS_ENABLE))
                .orElse(Lists.newArrayList());

        // 查询父级菜单 (一级菜单不需要)
        List<McMenu> parentList = null;
        if (!Objects.equals(MenuConsts.MENU_FIRST_LEVEL_PARENT_ID, menu.getParentId())) {
            parentList = menuBService.treeMenu();
        }

        ModelAndView mv = new ModelAndView("menu/detail");
        mv.addObject("menu", menu);
        mv.addObject("parentName", Objects.isNull(parentMenu) ? "" : parentMenu.getMenuName());
        mv.addObject("menuTypes", MenuConsts.listMenuType());
        mv.addObject("menuLevel", MenuConsts.listMenuLevel());
        mv.addObject("sysList", sysList);
        mv.addObject("parentList", parentList);

        return mv;
    }

    /**
     * 描述: 加载菜单
     *
     * @author guoqiao
     * @since 2021-05-28 16:23:56
     */
    @RequestMapping(value = "/loadMenu")
    public ResponseDTO<List<McMenu>> loadMenu() {
        try {
            // 从redis获取
            String key = RedisConsts.MENU + getStaffId();
            List<McMenu> menuRedis = (List<McMenu>) redisTemplate.opsForValue().get(key);
            if (!CollectionUtils.isEmpty(menuRedis)) {
                return ResponseDTO.success(menuRedis);
            }

            // 查询所有系统的域名
            Map<Integer, String> urlMap = Optional.ofNullable(sysService.listAll(GlobalConsts.YES))
                    .orElse(Collections.emptyList())
                    .stream()
                    .collect(Collectors.toMap(McSys::getSysId, McSys::getUrl));
            // 查询有效菜单
            McMenuQuery query = McMenuQuery.builder()
                    .status(GlobalConsts.YES)
                    .build();
            List<McMenu> menuOriList = Optional.ofNullable(menuService.listByQuery(query))
                    .orElse(Collections.emptyList())
                    .stream()
                    .peek(menu -> {
                        if (MenuConsts.MENU_TYPE_MENU_KEY == menu.getMenuType() && NumberUtil.isNotNullOrZero(menu.getSysId())) {
                            // url设置域名
                            menu.setMenuUrl(urlMap.get(menu.getSysId()) + menu.getMenuUrl());
                        }
                    }).collect(Collectors.toList());

            // 过滤权限
            List<McMenu> menuPermitList = menuOriList
                    .stream()
                    .filter(menu -> this.filterPermit(menu.getMenuPermitUrl(), menuPermitManager))
                    .collect(Collectors.toList());

            // 仅限类型为目录，按menuId分组的map集合
            Map<Integer, List<McMenu>> menuIdMap = menuPermitList.stream()
                    .filter(menu -> menu.getMenuType() == MenuConsts.MENU_TYPE_WHOLE_KEY).collect(Collectors.groupingBy(McMenu::getMenuId));
            // 仅限类型为菜单的集合
            List<McMenu> menuList = menuPermitList.stream().filter(menu -> menu.getMenuType() == MenuConsts.MENU_TYPE_MENU_KEY).collect(Collectors.toList());

            // 遍历过滤空包
            for (McMenu menu : menuList) {
                filterEmptyPack(menuIdMap, menu.getParentId());
            }
            // 从菜单集合中移除空包
            for (Map.Entry<Integer, List<McMenu>> entry : menuIdMap.entrySet()) {
                menuPermitList.remove(entry.getValue().get(0));
            }

            // 按parentId分组的map集合
            Map<Integer, List<McMenu>> parentIdMap = menuPermitList.stream().collect(Collectors.groupingBy(McMenu::getParentId));
            // 构建菜单树
            List<McMenu> treeMenus = this.buildTree(parentIdMap, GlobalConsts.LEVEL_1);

            // 同步redis
            if (!CollectionUtils.isEmpty(treeMenus)) {
                redisTemplate.opsForValue().set(key, treeMenus, 5, TimeUnit.MINUTES);
            }
            return ResponseDTO.success(treeMenus);
        } catch (Exception e) {
            log.error("MenuListController.loadMenu查询失败,原因:{}", e.getMessage(), e);
            return ResponseDTO.fail("查询失败，程序异常");
        }
    }

    /**
     * 描述: 过滤空包
     * <p>从菜单向上递归查询所有的父级，找到的从map中移除，递归完后剩余的就是空包</p>
     *
     * @param menuIddMap 按menuId分组的map集合
     * @param menuId     菜单id
     * @author guoqiao
     * @since 2021/6/3 11:52
     */
    public static void filterEmptyPack(Map<Integer, List<McMenu>> menuIddMap, Integer menuId) {
        List<McMenu> menuList = menuIddMap.get(menuId);
        if (!CollectionUtils.isEmpty(menuList)) {
            // 从map移除
            menuIddMap.remove(menuId);
            if (menuList.get(0).getParentId() != GlobalConsts.LEVEL_1) {
                filterEmptyPack(menuIddMap, menuList.get(0).getParentId());
            }
        }
    }

    /**
     * 描述: 判断是否有权限
     *
     * @param url 权限url
     * @return java.lang.Boolean
     * @author guoqiao
     * @since 2021/6/3 11:52
     */
    public static Boolean filterPermit(String url, MenuPermitManager menuPermitManager) {

        if (StringUtil.isBlank(url)) {
            return true;
        }
        return menuPermitManager.checkMenuPermit(url);
    }

    /**
     * 描述: 构建菜单树形结构
     *
     * @param parentIdMap 菜单集合
     * @param parentId    需要递归遍历的父级id
     * @return java.util.List<com.zmn.mcc.common.dto.menu.McMenuDRO>
     * @author guoqiao
     * @since 2021/6/02 10:32
     */
    public static List<McMenu> buildTree(Map<Integer, List<McMenu>> parentIdMap, int parentId) {
        List<McMenu> treeList = new ArrayList<>();
        List<McMenu> list = parentIdMap.get(parentId);
        if (!CollectionUtils.isEmpty(list)) {
            for (McMenu menu : list) {
                // 只有目录才设置子类
                if (menu.getMenuType() == MenuConsts.MENU_TYPE_WHOLE_KEY) {
                    menu.setSubMenuList(buildTree(parentIdMap, menu.getMenuId()));
                }
                treeList.add(menu);
            }
        }
        return treeList;
    }

}
